package com.self.operationav.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.PumpStreamHandler;
import org.springframework.util.StringUtils;
import ws.schild.jave.process.ffmpeg.DefaultFFMPEGLocator;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * {ffmpeg视频处理工具类}
 *
 * @author Ray
 * @since 1.0
 */
@Slf4j
public class FFmpegUtils {

    private static final DefaultFFMPEGLocator locator = new DefaultFFMPEGLocator();

    /**
     * m4a 转成 mp3
     * ffmpeg -i input.m4a -vn -ar 44100 -ab 128k -f mp3 output.mp3
     */
    public static void m4aToMp3(String filePath, String outputFilePath) throws NoSuchFieldException, IOException {
        File originFile = new File(filePath);
        String fileNameExcludeSuffix = extractFileNameExcludeSuffix(originFile.getName());

        String audioSuffix = "mp3";
        String outputFile = outputFilePath + File.separator + fileNameExcludeSuffix + "." + audioSuffix;
        List<String> commands = Arrays.asList(locator.getExecutablePath(), " -i", StrUtil.format("\"{}\"",filePath),
                "-vn -ar 44100 -ab 128k -f mp3", StrUtil.format("\"{}\"",outputFile));
        execCommand(commands);
    }

    /**
     * wma 转成 mp3
     * ffmpeg -i input.wma -vn -c:a libmp3lame output.mp3
     */
    public static void wmaToMp3(String filePath, String outputFilePath) throws NoSuchFieldException, IOException {
        File originFile = new File(filePath);
        String fileNameExcludeSuffix = extractFileNameExcludeSuffix(originFile.getName());

        String audioSuffix = "mp3";
        String outputFile = outputFilePath + File.separator + fileNameExcludeSuffix + "." + audioSuffix;
        List<String> commands = Arrays.asList(locator.getExecutablePath(), "-v warning -i", StrUtil.format("\"{}\"",filePath),
                "-vn -c:a libmp3lame", StrUtil.format("\"{}\"",outputFile));
        execCommand(commands);
    }


    /**
     * 无损提取音频
     * e.g. ffmpeg -i input.mp4 -vn -acodec copy output.aac
     *
     * @param filePath 源文件路径
     * @return {@code true} if the extract success
     */
    public static void extractAudio(String filePath) {
        try {
            File originFile = new File(filePath);
            String name = originFile.getName();
            String fileNameExcludeSuffix = extractFileNameExcludeSuffix(originFile.getName());

            String audioSuffix = "wav";
            String outputFilePath = originFile.getParent() + File.separator + fileNameExcludeSuffix + "." + audioSuffix;
            List<String> commands = Arrays.asList(locator.getExecutablePath(), "-v warning -i", filePath, " -ar 8000 -ac 1", outputFilePath);

            execCommand(commands);
        } catch (Exception e) {
            log.warn("extractAudio error msg:{}", e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * 无损提取无声视频
     * e.g. ffmpeg -i input.mp4 -an -vcodec copy out.put.mp4
     *
     * @param filePath 源文件路径
     * @return {@code true} if the extract success
     */
    public static void extractMuteVideo(String filePath) {
        try {
            File originFile = new File(filePath);
            String name = originFile.getName();
            String outputPath = originFile.getParent() + File.separator + "mute_" + name;

            List<String> commands = Arrays.asList(locator.getExecutablePath(), "-v warning -i", filePath, "-an -vcodec copy", outputPath);

            execCommand(commands);
        } catch (Exception e) {
            log.warn("extractMuteVideo error msg:{}", e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * ffmpeg -i input.mp4 -ar 8000 -ac 1 output.wav -an -vcodec copy output.mp4
     */
    public static String[] extractAudioAndMuteVideo(String filePath) {
        try {
            File originFile = new File(filePath);
            String name = originFile.getName();
            log.info("name:{}", name);
            String fileNameExcludeSuffix = extractFileNameExcludeSuffix(originFile.getName());

            String audioSuffix = "wav";
            String currentPath = originFile.getParent();

            String outputWav = currentPath + File.separator + fileNameExcludeSuffix + "." + audioSuffix;
            String outputVideo = currentPath + File.separator + "mute_" + name;

            List<String> commands = Arrays.asList(locator.getExecutablePath(), "-v warning -i", filePath, "-ar 8000 -ac 1", outputWav, "-an -vcodec copy", outputVideo);

            execCommand(commands);
            return new String[]{outputWav, outputVideo};
        } catch (Exception e) {
            log.warn("extractAudioAndMuteVideo error msg:{}", e.getMessage());
            throw new RuntimeException(e);
        }
    }


    /**
     * 获取没有文件格式后缀的文件名
     *
     * @param fileName 文件名
     * @return
     * @throws NoSuchFieldException
     */
    public static String extractFileNameExcludeSuffix(String fileName) throws NoSuchFieldException {
        if (StringUtils.isEmpty(fileName)) {
            log.warn("file name is invalid.");
            throw new NoSuchFieldException("file name is invalid.");
        }
        int index = fileName.lastIndexOf(".");
        return fileName.substring(0, index);
    }

    /**
     * 调用命令行执行，并返回信息 重复写？
     *
     * @param command 命令行参数
     */
    private static void execCommand(List<String> command) throws IOException {
        // 接收正常结果流
        ByteArrayOutputStream susStream = new ByteArrayOutputStream();
        // 接收异常结果流
        ByteArrayOutputStream errStream = new ByteArrayOutputStream();
        String commandStr = CollectionUtil.join(command, " ");
        try {
            if (log.isDebugEnabled()) {
                log.debug("ffmpeg command:{}", commandStr);
            }
            CommandLine commandLine = CommandLine.parse(commandStr);
            DefaultExecutor exec = new DefaultExecutor();
            PumpStreamHandler streamHandler = new PumpStreamHandler(susStream, errStream);
            exec.setStreamHandler(streamHandler);
            int code = exec.execute(commandLine);
            log.info("result code: {}", code);
            if (code != 0) {
                throw new RuntimeException("exec failed!");
            }
        } catch (Exception e) {
            String err = errStream.toString("GBK");
            log.warn("exec failed, err msg: {}", err);
            log.warn("cmd= {}", commandStr);
            throw new RuntimeException(e);
        }
    }
}
